Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\packet\question\mod.rs
Line
Count
Source
1
use crate::dns::q_class::DNSClass;
2
use crate::dns::q_name::parse_qname;
3
use crate::dns::q_type::DNSRecordType;
4
use crate::exceptions::SCloudException;
5
6
/// A DNS Question section
7
#[derive(Debug, PartialEq, Clone)]
8
pub struct QuestionSection {
9
    pub q_name: String,
10
    pub q_type: DNSRecordType,
11
    pub q_class: DNSClass,
12
}
13
14
impl QuestionSection {
15
    /// Serialize the DNS question section into a byte array
16
    ///
17
    /// # Exemple :
18
    /// ```
19
    /// use crate::dns::packet::question::QuestionSection;
20
    /// use crate::dns::q_type::DNSRecordType;
21
    /// use crate::dns::q_class::DNSClass;
22
    ///
23
    /// let question = QuestionSection {
24
    ///     q_name: "example.com".to_string(),
25
    ///     q_type: DNSRecordType::A,
26
    ///     q_class: DNSClass::IN,
27
    /// };
28
    ///
29
    /// let bytes = question.to_bytes().unwrap();
30
    ///
31
    /// // QNAME + QTYPE + QCLASS
32
    /// assert!(bytes.len() > 6);
33
    /// ```
34
3
    pub fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> {
35
3
        let mut buf = Vec::with_capacity(self.q_name.len() + 5);
36
37
        // Encode QNAME
38
6
        for label in 
self.q_name.split('.')3
{
39
6
            let len = label.len();
40
6
            if len > 63 {
41
1
                return Err(SCloudException::SCLOUD_QUESTION_SERIALIZATION_FAILED_QNAME_TOO_LONG);
42
5
            }
43
5
            buf.push(len as u8);
44
5
            buf.extend_from_slice(label.as_bytes());
45
        }
46
2
        buf.push(0x00);
47
48
2
        let q_type_u16 =
49
2
            u16::try_from(self.q_type).expect("Cannot convert QuestionSection q_type to u16");
50
2
        let q_class_u16 = u16::try_from(self.q_class).unwrap();
51
52
2
        buf.extend_from_slice(&q_type_u16.to_be_bytes());
53
2
        buf.extend_from_slice(&q_class_u16.to_be_bytes());
54
55
2
        Ok(buf)
56
3
    }
57
58
    /// Deserialize the DNS question section from a byte array
59
    ///
60
    /// # Exemple :
61
    /// ```
62
    /// use crate::dns::packet::question::QuestionSection;
63
    /// use crate::dns::q_type::DNSRecordType;
64
    /// use crate::dns::q_class::DNSClass;
65
    ///
66
    /// // example.com A IN
67
    /// let raw_question: Vec<u8> = vec![
68
    ///     0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
69
    ///     0x03, b'c', b'o', b'm',
70
    ///     0x00,       // End of QNAME
71
    ///     0x00, 0x01, // QTYPE = A
72
    ///     0x00, 0x01, // QCLASS = IN
73
    /// ];
74
    ///
75
    /// let (question, consumed) = QuestionSection::from_bytes(&raw_question, 0).unwrap();
76
    ///
77
    /// assert_eq!(question.q_name, "example.com");
78
    /// assert_eq!(question.q_type, DNSRecordType::A);
79
    /// assert_eq!(question.q_class, DNSClass::IN);
80
    /// assert_eq!(consumed, raw_question.len());
81
    /// ```
82
2
    pub fn from_bytes(
83
2
        buf: &[u8],
84
2
        offset: usize,
85
2
    ) -> Result<(QuestionSection, usize), SCloudException> {
86
2
        let (q_name, mut pos) = parse_qname(buf, offset).unwrap();
87
88
2
        if buf.len() < pos + 4 {
89
0
            return Err(SCloudException::SCLOUD_QUESTION_DESERIALIZATION_FAILED);
90
2
        }
91
92
2
        let q_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
93
94
2
        let q_class = DNSClass::try_from(u16::from_be_bytes([buf[pos + 2], buf[pos + 3]])).unwrap();
95
96
2
        pos += 4;
97
98
2
        Ok((
99
2
            QuestionSection {
100
2
                q_name,
101
2
                q_type,
102
2
                q_class,
103
2
            },
104
2
            pos - offset,
105
2
        ))
106
2
    }
107
}